/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.rest;

import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT;
import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT;
import static org.dspace.content.service.DSpaceObjectService.MD_LICENSE;
import static org.dspace.content.service.DSpaceObjectService.MD_NAME;
import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION;
import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Constants;
import org.dspace.core.LogManager;
import org.dspace.rest.common.Collection;
import org.dspace.rest.common.Item;
import org.dspace.rest.common.MetadataEntry;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.usage.UsageEvent;
import org.dspace.workflow.WorkflowService;
import org.dspace.workflow.factory.WorkflowServiceFactory;

/**
 * This class provides all CRUD operation over collections.
 *
 * @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
 */
@Path("/collections")
public class CollectionsResource extends Resource {
    protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
    protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
    protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
    protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
    protected WorkflowService workflowService = WorkflowServiceFactory.getInstance().getWorkflowService();

    private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(CollectionsResource.class);

    /**
     * Return instance of collection with passed id. You can add more properties
     * through expand parameter.
     *
     * @param collectionId  Id of collection in DSpace.
     * @param expand        String in which is what you want to add to returned instance
     *                      of collection. Options are: "all", "parentCommunityList",
     *                      "parentCommunity", "items", "license" and "logo". If you want
     *                      to use multiple options, it must be separated by commas.
     * @param limit         Limit value for items in list in collection. Default value is
     *                      100.
     * @param offset        Offset of start index in list of items of collection. Default
     *                      value is 0.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collection as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return Return instance of collection. It can also return status code
     * NOT_FOUND(404) if id of collection is incorrect or status code
     * UNATHORIZED(401) if user has no permission to read collection.
     * @throws WebApplicationException It is thrown when was problem with database reading
     *                                 (SQLException) or problem with creating
     *                                 context(ContextException). It is thrown by NOT_FOUND and
     *                                 UNATHORIZED status codes, too.
     */
    @GET
    @Path("/{collection_id}")
    @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public org.dspace.rest.common.Collection getCollection(@PathParam("collection_id") String collectionId,
                                                           @QueryParam("expand") String expand,
                                                           @QueryParam("limit") @DefaultValue("100") Integer limit,
                                                           @QueryParam("offset") @DefaultValue("0") Integer offset,
                                                           @QueryParam("userIP") String user_ip,
                                                           @QueryParam("userAgent") String user_agent,
                                                           @QueryParam("xforwardedfor") String xforwardedfor,
                                                           @Context HttpHeaders headers,
                                                           @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Reading collection(id=" + collectionId + ").");
        org.dspace.core.Context context = null;
        Collection collection = null;

        try {
            context = createContext();

            org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
                                                                            org.dspace.core.Constants.READ);
            writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
                       headers, request, context);

            collection = new Collection(dspaceCollection, servletContext, expand, context, limit, offset);
            context.complete();

        } catch (SQLException e) {
            processException("Could not read collection(id=" + collectionId + "), SQLException. Message: " + e,
                             context);
        } catch (ContextException e) {
            processException(
                "Could not read collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
                context);
        } finally {
            processFinally(context);
        }

        log.trace("Collection(id=" + collectionId + ") has been successfully read.");
        return collection;
    }

    /**
     * Return array of all collections in DSpace. You can add more properties
     * through expand parameter.
     *
     * @param expand        String in which is what you want to add to returned instance
     *                      of collection. Options are: "all", "parentCommunityList",
     *                      "parentCommunity", "items", "license" and "logo". If you want
     *                      to use multiple options, it must be separated by commas.
     * @param limit         Limit value for items in list in collection. Default value is
     *                      100.
     * @param offset        Offset of start index in list of items of collection. Default
     *                      value is 0.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collections as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return Return array of collection, on which has logged user permission
     * to view.
     * @throws WebApplicationException It is thrown when was problem with database reading
     *                                 (SQLException) or problem with creating
     *                                 context(ContextException).
     */
    @GET
    @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public org.dspace.rest.common.Collection[] getCollections(@QueryParam("expand") String expand,
                                                              @QueryParam("limit") @DefaultValue("100") Integer limit,
                                                              @QueryParam("offset") @DefaultValue("0") Integer offset,
                                                              @QueryParam("userIP") String user_ip,
                                                              @QueryParam("userAgent") String user_agent,
                                                              @QueryParam("xforwardedfor") String xforwardedfor,
                                                              @Context HttpHeaders headers,
                                                              @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Reading all collections.(offset=" + offset + ",limit=" + limit + ")");
        org.dspace.core.Context context = null;
        List<Collection> collections = new ArrayList<>();

        try {
            context = createContext();

            if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) {
                log.warn("Paging was badly set.");
                limit = 100;
                offset = 0;
            }

            List<org.dspace.content.Collection> dspaceCollections = collectionService.findAll(context, limit, offset);
            for (org.dspace.content.Collection dspaceCollection : dspaceCollections) {
                if (authorizeService
                    .authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) {
                    Collection collection = new org.dspace.rest.common.Collection(dspaceCollection, servletContext,
                                                                                  null, context, limit,
                                                                                  offset);
                    collections.add(collection);
                    writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent,
                               xforwardedfor, headers, request, context);
                }
            }
            context.complete();
        } catch (SQLException e) {
            processException("Something went wrong while reading collections from database. Message: " + e, context);
        } catch (ContextException e) {
            processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(),
                             context);
        } finally {
            processFinally(context);
        }

        log.trace("All collections were successfully read.");
        return collections.toArray(new org.dspace.rest.common.Collection[0]);
    }

    /**
     * Return array of items in collection. You can add more properties to items
     * with expand parameter.
     *
     * @param collectionId  Id of collection in DSpace.
     * @param expand        String which define, what additional properties will be in
     *                      returned item. Options are separeted by commas and are: "all",
     *                      "metadata", "parentCollection", "parentCollectionList",
     *                      "parentCommunityList" and "bitstreams".
     * @param limit         Limit value for items in array. Default value is 100.
     * @param offset        Offset of start index in array of items of collection. Default
     *                      value is 0.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collection as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return Return array of items, on which has logged user permission to
     * read. It can also return status code NOT_FOUND(404) if id of
     * collection is incorrect or status code UNATHORIZED(401) if user
     * has no permission to read collection.
     * @throws WebApplicationException It is thrown when was problem with database reading
     *                                 (SQLException) or problem with creating
     *                                 context(ContextException). It is thrown by NOT_FOUND and
     *                                 UNATHORIZED status codes, too.
     */
    @GET
    @Path("/{collection_id}/items")
    @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public org.dspace.rest.common.Item[] getCollectionItems(@PathParam("collection_id") String collectionId,
                                                            @QueryParam("expand") String expand,
                                                            @QueryParam("limit") @DefaultValue("100") Integer limit,
                                                            @QueryParam("offset") @DefaultValue("0") Integer offset,
                                                            @QueryParam("userIP") String user_ip,
                                                            @QueryParam("userAgent") String user_agent,
                                                            @QueryParam("xforwardedfor") String xforwardedfor,
                                                            @Context HttpHeaders headers,
                                                            @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Reading collection(id=" + collectionId + ") items.");
        org.dspace.core.Context context = null;
        List<Item> items = null;

        try {
            context = createContext();

            org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
                                                                            org.dspace.core.Constants.READ);
            writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
                       headers, request, context);

            items = new ArrayList<>();
            Iterator<org.dspace.content.Item> dspaceItems = itemService.findByCollection(context, dspaceCollection,
                                                                                         limit, offset);

            while (dspaceItems.hasNext()) {
                org.dspace.content.Item dspaceItem = dspaceItems.next();

                if (itemService.isItemListedForUser(context, dspaceItem)) {
                    items.add(new Item(dspaceItem, servletContext, expand, context));
                    writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
                               headers, request, context);
                }
            }

            context.complete();
        } catch (SQLException e) {
            processException("Could not read collection items, SQLException. Message: " + e, context);
        } catch (ContextException e) {
            processException("Could not read collection items, ContextException. Message: " + e.getMessage(), context);
        } finally {
            processFinally(context);
        }

        log.trace("All items in collection(id=" + collectionId + ") were successfully read.");
        return items.toArray(new Item[0]);
    }

    /**
     * Create item in collection. Item can be without filled metadata.
     *
     * @param collectionId  Id of collection in which will be item created.
     * @param item          Item filled only with metadata, other variables are ignored.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collection as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return Return status code with item. Return status (OK)200 if item was
     * created. NOT_FOUND(404) if id of collection does not exists.
     * UNAUTHORIZED(401) if user have not permission to write items in
     * collection.
     * @throws WebApplicationException It is thrown when was problem with database reading or
     *                                 writing (SQLException) or problem with creating
     *                                 context(ContextException) or problem with authorization to
     *                                 collection or IOException or problem with index item into
     *                                 browse index. It is thrown by NOT_FOUND and UNATHORIZED
     *                                 status codes, too.
     */
    @POST
    @Path("/{collection_id}/items")
    @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Item addCollectionItem(@PathParam("collection_id") String collectionId, Item item,
                                  @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
                                  @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
                                  @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Create item in collection(id=" + collectionId + ").");
        org.dspace.core.Context context = null;
        Item returnItem = null;

        try {
            context = createContext();
            org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
                                                                            org.dspace.core.Constants.WRITE);

            writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
                       headers, request, context);

            log.trace("Creating item in collection(id=" + collectionId + ").");
            org.dspace.content.WorkspaceItem workspaceItem = workspaceItemService
                .create(context, dspaceCollection, false);
            org.dspace.content.Item dspaceItem = workspaceItem.getItem();

            log.trace("Adding metadata to item(id=" + dspaceItem.getID() + ").");
            if (item.getMetadata() != null) {
                for (MetadataEntry entry : item.getMetadata()) {
                    String data[] = mySplit(entry.getKey());
                    itemService.addMetadata(context, dspaceItem, data[0], data[1], data[2], entry.getLanguage(),
                                            entry.getValue());
                }
            }

            workspaceItemService.update(context, workspaceItem);

            try {
                // Must insert the item into workflow
                log.trace("Starting workflow for item(id=" + dspaceItem.getID() + ").");
                workflowService.start(context, workspaceItem);
            } catch (Exception e) {
                log.error(
                    LogManager.getHeader(context, "Error while starting workflow", "Item id: " + dspaceItem.getID()),
                    e);
                throw new ContextException("Error while starting workflow for item(id=" + dspaceItem.getID() + ")", e);
            }

            returnItem = new Item(workspaceItem.getItem(), servletContext, "", context);

            context.complete();

        } catch (SQLException e) {
            processException("Could not add item into collection(id=" + collectionId + "), SQLException. Message: " + e,
                             context);
        } catch (AuthorizeException e) {
            processException(
                "Could not add item into collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
                context);
        } catch (ContextException e) {
            processException(
                "Could not add item into collection(id=" + collectionId + "), ContextException. Message: " + e
                    .getMessage(),
                context);
        } finally {
            processFinally(context);
        }

        log.info(
            "Item successfully created in collection(id=" + collectionId + "). Item handle=" + returnItem.getHandle());
        return returnItem;
    }

    /**
     * Update collection. It replace all properties.
     *
     * @param collectionId  Id of collection in DSpace.
     * @param collection    Collection which will replace properties of actual collection.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collection as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return Return response 200 if was everything all right. Otherwise 400
     * when id of community was incorrect or 401 if was problem with
     * permission to write into collection.
     * @throws WebApplicationException It is thrown when was problem with database reading or
     *                                 writing. Or problem with authorization to collection. Or
     *                                 problem with creating context.
     */
    @PUT
    @Path("/{collection_id}")
    @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response updateCollection(@PathParam("collection_id") String collectionId,
                                     org.dspace.rest.common.Collection collection, @QueryParam("userIP") String user_ip,
                                     @QueryParam("userAgent") String user_agent,
                                     @QueryParam("xforwardedfor") String xforwardedfor,
                                     @Context HttpHeaders headers, @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Updating collection(id=" + collectionId + ").");
        org.dspace.core.Context context = null;

        try {
            context = createContext();
            org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
                                                                            org.dspace.core.Constants.WRITE);

            writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
                       headers, request, context);

            collectionService.setMetadataSingleValue(context, dspaceCollection,
                    MD_NAME, collection.getName(), null);
            collectionService.setMetadataSingleValue(context, dspaceCollection,
                    MD_LICENSE, collection.getLicense(), null);

            // dspaceCollection.setLogo(collection.getLogo()); // TODO Add this option.
            collectionService.setMetadataSingleValue(context, dspaceCollection,
                    MD_COPYRIGHT_TEXT, collection.getCopyrightText(), null);
            collectionService.setMetadataSingleValue(context, dspaceCollection,
                    MD_INTRODUCTORY_TEXT, collection.getIntroductoryText(), null);
            collectionService.setMetadataSingleValue(context, dspaceCollection,
                    MD_SHORT_DESCRIPTION, collection.getShortDescription(), null);
            collectionService.setMetadataSingleValue(context, dspaceCollection,
                    MD_SIDEBAR_TEXT, collection.getSidebarText(), null);
            collectionService.update(context, dspaceCollection);

            context.complete();

        } catch (ContextException e) {
            processException(
                "Could not update collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
                context);
        } catch (SQLException e) {
            processException("Could not update collection(id=" + collectionId + "), SQLException. Message: " + e,
                             context);
        } catch (AuthorizeException e) {
            processException("Could not update collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
                             context);
        } finally {
            processFinally(context);
        }

        log.info("Collection(id=" + collectionId + ") successfully updated.");
        return Response.ok().build();
    }

    /**
     * Delete collection.
     *
     * @param collectionId  Id of collection which will be deleted.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collection as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return Return response code OK(200) if was everything all right.
     * Otherwise return NOT_FOUND(404) if was id of community or
     * collection incorrect. Or (UNAUTHORIZED)401 if was problem with
     * permission to community or collection.
     * @throws WebApplicationException Thrown if there was a problem with creating context or problem
     *                                 with database reading or writing. Or problem with deleting
     *                                 collection caused by IOException or authorization.
     */
    @DELETE
    @Path("/{collection_id}")
    @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response deleteCollection(@PathParam("collection_id") String collectionId,
                                     @QueryParam("userIP") String user_ip,
                                     @QueryParam("userAgent") String user_agent,
                                     @QueryParam("xforwardedfor") String xforwardedfor,
                                     @Context HttpHeaders headers, @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Delete collection(id=" + collectionId + ").");
        org.dspace.core.Context context = null;

        try {
            context = createContext();
            org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
                                                                            org.dspace.core.Constants.DELETE);

            writeStats(dspaceCollection, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor,
                       headers, request, context);

            collectionService.delete(context, dspaceCollection);
            collectionService.update(context, dspaceCollection);

            context.complete();
        } catch (ContextException e) {
            processException(
                "Could not delete collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
                context);
        } catch (SQLException e) {
            processException("Could not delete collection(id=" + collectionId + "), SQLException. Message: " + e,
                             context);
        } catch (AuthorizeException e) {
            processException("Could not delete collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
                             context);
        } catch (IOException e) {
            processException("Could not delete collection(id=" + collectionId + "), IOException. Message: " + e,
                             context);
        } finally {
            processFinally(context);
        }

        log.info("Collection(id=" + collectionId + ") was successfully deleted.");
        return Response.ok().build();
    }

    /**
     * Delete item in collection.
     *
     * @param collectionId  Id of collection which will be deleted.
     * @param itemId        Id of item in colletion.
     * @param user_ip       User's IP address.
     * @param user_agent    User agent string (specifies browser used and its version).
     * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
     *                      source of the request. The proxy may be configured to add the
     *                      "X-Forwarded-For" HTTP header containing the original IP of the client
     *                      so that the reverse-proxied application can get the client's IP.
     * @param headers       If you want to access the collection as the user logged into the
     *                      context. The value of the "rest-dspace-token" header must be set
     *                      to the token received from the login method response.
     * @param request       Servlet's HTTP request object.
     * @return It returns status code: OK(200). NOT_FOUND(404) if item or
     * collection was not found, UNAUTHORIZED(401) if user is not
     * allowed to delete item or permission to write into collection.
     * @throws WebApplicationException It can be thrown by: SQLException, when was problem with
     *                                 database reading or writting. AuthorizeException, when was
     *                                 problem with authorization to item or collection.
     *                                 IOException, when was problem with removing item.
     *                                 ContextException, when was problem with creating context of
     *                                 DSpace.
     */
    @DELETE
    @Path("/{collection_id}/items/{item_id}")
    @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response deleteCollectionItem(@PathParam("collection_id") String collectionId,
                                         @PathParam("item_id") String itemId,
                                         @QueryParam("userIP") String user_ip,
                                         @QueryParam("userAgent") String user_agent,
                                         @QueryParam("xforwardedfor") String xforwardedfor,
                                         @Context HttpHeaders headers, @Context HttpServletRequest request)
        throws WebApplicationException {

        log.info("Delete item(id=" + itemId + ") in collection(id=" + collectionId + ").");
        org.dspace.core.Context context = null;

        try {
            context = createContext();

            org.dspace.content.Collection dspaceCollection = collectionService
                .findByIdOrLegacyId(context, collectionId);
            org.dspace.content.Item item = itemService.findByIdOrLegacyId(context, itemId);


            if (dspaceCollection == null) {
                //throw collection not exist
                log.warn("Collection(id=" + itemId + ") was not found!");
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            }

            if (item == null) {
                //throw item not exist
                log.warn("Item(id=" + itemId + ") was not found!");
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            }

            if (!authorizeService.authorizeActionBoolean(context, item, Constants.REMOVE)
                || !authorizeService.authorizeActionBoolean(context, dspaceCollection, Constants.REMOVE)) {
                //throw auth
                if (context.getCurrentUser() != null) {
                    log.error(
                        "User(" + context.getCurrentUser().getEmail() + ") does not have permission to delete item!");
                } else {
                    log.error("User(anonymous) has not permission to delete item!");
                }
                throw new WebApplicationException(Response.Status.UNAUTHORIZED);
            }

            collectionService.removeItem(context, dspaceCollection, item);
            collectionService.update(context, dspaceCollection);
            itemService.update(context, item);

            writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
                       headers, request, context);
            writeStats(item, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor, headers, request, context);

            context.complete();

        } catch (ContextException e) {
            processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
                                 + "), ContextException. Message: " + e.getMessage(), context);
        } catch (SQLException e) {
            processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
                                 + "), SQLException. Message: " + e, context);
        } catch (AuthorizeException e) {
            processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
                                 + "), AuthorizeException. Message: " + e, context);
        } catch (IOException e) {
            processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
                                 + "), IOException. Message: " + e, context);
        } finally {
            processFinally(context);
        }

        log.info("Item(id=" + itemId + ") in collection(id=" + collectionId + ") was successfully deleted.");
        return Response.ok().build();
    }

    /**
     * Search for first collection with passed name.
     *
     * @param name    Name of collection.
     * @param headers If you want to access the collection as the user logged into the
     *                context. The value of the "rest-dspace-token" header must be set
     *                to the token received from the login method response.
     * @return It returns null if collection was not found. Otherwise returns
     * first founded collection.
     * @throws WebApplicationException A general exception a servlet can throw when it encounters difficulty.
     */
    @POST
    @Path("/find-collection")
    @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Collection findCollectionByName(String name, @Context HttpHeaders headers) throws WebApplicationException {
        log.info("Searching for first collection with name=" + name + ".");
        org.dspace.core.Context context = null;
        Collection collection = null;

        try {
            context = createContext();

            List<org.dspace.content.Collection> dspaceCollections = collectionService.findAll(context);
            //TODO, this would be more efficient with a findByName query

            for (org.dspace.content.Collection dspaceCollection : dspaceCollections) {
                if (authorizeService
                    .authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) {
                    if (dspaceCollection.getName().equals(name)) {
                        collection = new Collection(dspaceCollection, servletContext, "", context, 100, 0);
                        break;
                    }
                }
            }

            context.complete();

        } catch (SQLException e) {
            processException(
                "Something went wrong while searching for collection(name=" + name + ") from database. Message: "
                    + e, context);
        } catch (ContextException e) {
            processException(
                "Something went wrong while searching for collection(name=" + name + "), ContextError. Message: "
                    + e.getMessage(), context);
        } finally {
            processFinally(context);
        }

        if (collection == null) {
            log.info("Collection was not found.");
        } else {
            log.info("Collection was found with id(" + collection.getUUID() + ").");
        }
        return collection;
    }

    /**
     * Find collection from DSpace database. It is encapsulation of method
     * org.dspace.content.Collection.find with checking if item exist and if
     * user logged into context has permission to do passed action.
     *
     * @param context Context of actual logged user.
     * @param id      Id of collection in DSpace.
     * @param action  Constant from org.dspace.core.Constants.
     * @return It returns DSpace collection.
     * @throws WebApplicationException Is thrown when item with passed id is not exists and if user
     *                                 has no permission to do passed action.
     */
    private org.dspace.content.Collection findCollection(org.dspace.core.Context context, String id, int action)
        throws WebApplicationException {
        org.dspace.content.Collection collection = null;
        try {
            collection = collectionService.findByIdOrLegacyId(context, id);

            if (collection == null) {
                context.abort();
                log.warn("Collection(id=" + id + ") was not found!");
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            } else if (!authorizeService.authorizeActionBoolean(context, collection, action)) {
                context.abort();
                if (context.getCurrentUser() != null) {
                    log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to "
                                  + getActionString(action) + " collection!");
                } else {
                    log.error("User(anonymous) has not permission to " + getActionString(action) + " collection!");
                }
                throw new WebApplicationException(Response.Status.UNAUTHORIZED);
            }

        } catch (SQLException e) {
            processException("Something get wrong while finding collection(id=" + id + "). SQLException, Message: " + e,
                             context);
        }
        return collection;
    }
}
